/*
 * @file: device.c
 * @brief: 
 * @author: 廖文雄 
 * @date: 2019.10.30
 * @note: 
 */
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <memory.h>
#include <stdint.h>
#include <pthread.h>
#include <time.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <unistd.h>

#include "device.h"
#include "ioctl.h"
#include "debug.h"
#include "communication.h"
#include "func_device.h"

static char *sub_string(char* ch, int pos, int length);
static int is_num(char *str);
static int is_uppercase_letter(char *str);
static void *handle_request(void *arg);
static void *handle_connect(void *arg);
static void *handle_work(void *arg);

/**
 * @brief   socket套接字的初始化
 * @param   socket_param：用户输入的套接字参数
 * @return  
 */
int socket_init(struct socket_param *socket_param)
{
    struct sockaddr_in s_addr;
    int socket_fd;

    if (!socket_param) {
        DEBUG("SOCKET_INIT PARAM FALSE!\n");
        return PARAM_FALSE;
    }

    /* 建立socket */
    if ((socket_fd = socket(socket_param->socket_domain, socket_param->socket_type,
        0)) == -1) {
        DEBUG("socket create error!\n");
        return SOCKET_ERROR;
    } else {
        printf("socket create success!\n");
    }

    /* 设置服务器ip */
    bzero(&s_addr, sizeof(s_addr));
    s_addr.sin_family = socket_param->socket_domain;
    s_addr.sin_port = htons(socket_param->socket_port_num);
    s_addr.sin_addr.s_addr = inet_addr(socket_param->ipv4_address);

    /* 把地址和端口绑定到套接字上 */
    if ((bind(socket_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr)))
        == -1) {
        DEBUG("BIND FALSE!\n");
        return BIND_ERROR;
    } else {
        printf("bind success!\n");
    }

    /* 侦听本地端口 */
    if (listen(socket_fd, socket_param->listen_num) == -1) {
        DEBUG("LISTEN FALSE!\n");
        return LISTEN_ERROR;
    } else {
        printf("the server is listening!\n");
    }

    return socket_fd;
}

/**
 * @brief   打开socket通信
 * @param   socket_fd：socket的描述符
 * @return  
 */
int open_socket(int socket_fd)
{
    pthread_t thread_s[2];
    struct pthread_arg *pthread_arg = (struct pthread_arg *)
        malloc(sizeof(struct pthread_arg));
    int connect_host[MAX_CLIENT_NUM];
    
    pthread_arg->connect_host = connect_host;
    pthread_arg->socket_fd = socket_fd;

    /* 创建线程处理客户端的连接 */
    pthread_create(&thread_s[0], NULL, handle_connect, (void *)pthread_arg);

    /* 创建线程处理客户端的请求 */
    pthread_create(&thread_s[1], NULL, handle_request, (void *)pthread_arg);

    return TRUE;
}

/**
 * @brief   处理客户端的连接
 * @param   arg->connect_host(用来保存连接套接字的数组)
 *          arg->socket(通信套接字)
 * @return  
 */
static void *handle_connect(void *arg)
{
    struct pthread_arg *pthread_arg = (struct pthread_arg *)arg;

    int new_fd;
    struct sockaddr_in c_addr;
    socklen_t len;
    int i;

    while (1) {
        len = sizeof(struct sockaddr);
        if ((new_fd = accept(pthread_arg->socket_fd,
            (struct sockaddr *)&c_addr,
            &len)) > 0) {
            printf("\n*****通信开始*****\n");
            printf("正在通信的客户端是:%s:%d\n", inet_ntoa(c_addr.sin_addr),
                ntohs(c_addr.sin_port));

            for (i = 0; i < MAX_CLIENT_NUM; i++) {
                if (pthread_arg->connect_host[i] == -1) {
                    pthread_arg->connect_host[i] = new_fd;
                    /* 客户端计数器 */
                    pthread_arg->connect_num++;
                    /* 退出内层循环，重新进行最外层循环，继续等待新连接的客户端 */
                    break;
                }
            }
        }
    }
}

/**
 * @brief   处理客户端的请求
 * @param   arg->connect_host(用来保存连接套接字的数组)
 *          arg->socket(通信套接字)
 * @return  
 */
static void *handle_request(void *arg)
{
    struct pthread_arg *pthread_arg = (struct pthread_arg *)arg;
    pthread_t work_thread;
    int socket_fd;
    int last_fd = -1;
    fd_set rfds;

    struct timeval tv;
    tv.tv_sec = 1;
    tv.tv_usec = 0;

    int i = 0;
    int err = -1;

    while (1) {
        FD_ZERO(&rfds);
        for (i = 0; i < MAX_CLIENT_NUM; i++) {
            if (pthread_arg->connect_host[i] != -1) {
                /* 将fd加入set合集 */
                FD_SET(pthread_arg->connect_host[i], &rfds);
                /* 找到最后一个fd */
                if (last_fd < pthread_arg->connect_host[i]) {
                    last_fd = pthread_arg->connect_host[i];
                }
            }
        }

        err = select(last_fd + 1, &rfds, NULL, NULL, &tv);
        switch (err) {
            case SELECT_TIME_OUT:
                break;
            case SELECT_ERROR:
                break;
            default:
                if (pthread_arg->connect_num < 0) {
                    break;
                }
                for (i = 0; i < MAX_CLIENT_NUM; i++) {
                    if (pthread_arg->connect_host[i] != -1) {
                        /* FD_ISSET:
                            判断描述符fd是否在指定的描述符集fdset中 */
                        if (FD_ISSET(pthread_arg->connect_host[i], &rfds)) {
                            /* 每个循环创建一个服务端工作线程处理 */
                            socket_fd = pthread_arg->connect_host[i];
                            pthread_create(&work_thread, NULL, handle_work,
                                (void *)&socket_fd);

                            /* 关闭通讯的套接字 */
                            close(pthread_arg->connect_host[i]);

                            /*更新文件描述符在数组中的值*/
                            pthread_arg->connect_host[i] = -1;
                            pthread_arg->connect_num--;
                        }
                    }
                }
        }
    }

    return 0;
}

/**
 * @brief   处理连接后的工作
 * @return  符合则返回1，不符合返回对应的宏定义
 */
static void *handle_work(void *arg)
{
    int *socket_fd = (int *)arg;
    int ret, cmd;
    char *need_replace_buff;

    /* 等待、接收来自客户端的数据 */
    while (1) {
        ret = recv(*socket_fd, need_replace_buff, sizeof(struct top_protocol), 0);
        if (ret == SOCKET_ERROR) {
            DEBUG("INTERNET ERROR\n");
        }//else if
        if (ret != SOCKET_ERROR && ret != 0) {
            /* 接收到数据则跳出循环 */
            break;
        }
    }

    cmd = communication_top_analysis(need_replace_buff);
    switch (cmd) {
        case LOGIN_REQUEST:
            server_login(*socket_fd);
            break;
        case REGISTER_REQUEST:
            server_register(*socket_fd);
            break;
        case ADD_FRIEND_REQUEST:
            server_add_friend(*socket_fd);
            break;
        case DELETE_FRIEND_REQUEST:
            server_delete_friend(*socket_fd);
            break;
        case SEND_MESSAGE_REQUEST:
            server_send_message(*socket_fd);
            break;
    }
}

/**
 * @brief   检测输入的socket函数的参数是否符合规范
 * @param   argv：输入的socket函数的参数
 * @return  符合则返回1，不符合返回对应的宏定义
 */
int ckeck_user_input_param(char *argv[])
{
    struct user_input_param *user_input_param = (struct user_input_param *)
        malloc(sizeof(struct user_input_param));
    char *ipv4_address = argv[3], *intercept;
    int count = 0, segment_head = 0, segment_tail = 0, ret = 0, cycle_times = 0;

    if (!argv) {
        DEBUG("USER INPUT PARAM ERROR!\n");
        return PARAM_FALSE;
    }

    if (0 != memcmp(argv[1], "SOCK_DGRAM", strlen("SOCK_DGRAM\0"))          &&
        0 != memcmp(argv[1], "SOCK_RAW", strlen("SOCK_RAW\0"))              &&
        0 != memcmp(argv[1], "SOCK_SEQPACKET", strlen("SOCK_SEQPACKET\0"))  &&
        0 != memcmp(argv[1], "SOCK_STREAM", strlen("SOCK_STREAM\0"))) {

        printf("socket type error!\n");
        return SOCKET_TYPE_ERROR;
    } else if (!is_uppercase_letter(argv[1])) {
        printf("socket type error!\n");
        return SOCKET_TYPE_ERROR;
    }

    if (0 != memcmp(argv[2], "AF_INET", strlen("AF_INET\0"))   &&
        0 != memcmp(argv[2], "AF_INET6", strlen("AF_INET6\0")) &&
        0 != memcmp(argv[2], "AF_UNIX", strlen("AF_UNIX\0"))   &&
        0 != memcmp(argv[2], "AF_UNSPEC", strlen("AF_UNSPEC\0"))) {

        printf("socket domain error!\n");
        return SOCKET_DOMAIN_ERROR;
    } else if (!is_uppercase_letter(argv[2])) {
        printf("socket domain error!\n");
        return SOCKET_DOMAIN_ERROR;
    }

    /*需满足IPv4格式*/
    /*使用字符串截取*/
    if (ipv4_address[strlen(argv[3]) - 1] == '.') {
        printf("socket ipv4 address error!\n");
        return SOCKET_IPV4_ADDRESS_ERROR;
    }
    for (; count <= strlen(argv[3]); count++) {
        if (ipv4_address[count] == '.' || count == strlen(argv[3])) {
            segment_tail = count;
            intercept = sub_string(argv[3], segment_head,
                segment_tail - segment_head);
            /*检测是否是数字*/
            ret = is_num(intercept);
            if (!ret) {
                printf("socket ipv4 address error!\n");
                return SOCKET_IPV4_ADDRESS_ERROR;
            } else if (0 > atoi(intercept) || 255 < atoi(intercept)) {
                printf("socket ipv4 address error!\n");
                return SOCKET_IPV4_ADDRESS_ERROR;
            }

            segment_head = segment_tail + 1;
            cycle_times++;
        }
    }
    if (IPV4_ADDRESS_NUM != cycle_times) {
        printf("socket ipv4 address error!\n");
        return SOCKET_IPV4_ADDRESS_ERROR;
    }

    if (65535 < atoi(argv[4]) && atoi(argv[4]) > 0) {
        printf("socket port error!\n");
        return SOCKET_PORT_ERROR;
    } else if (!is_num(argv[4])) {
        printf("socket port error!\n");
        return SOCKET_PORT_ERROR;
    }

    if (atoi(argv[5]) < 0) {
        printf("socket listen number error!\n");
        return SOCKET_LISTEN_NUM_ERROR;
    } else if (!is_num(argv[5])) {
        printf("socket listen number error!\n");
        return SOCKET_LISTEN_NUM_ERROR;
    }

    user_input_param->socket_type = argv[1];
    user_input_param->socket_domain = argv[2];
    user_input_param->ipv4_address = argv[3];
    user_input_param->socket_port_num = argv[4];
    user_input_param->listen_num = argv[5];

    return TRUE;
}

/**
 * @brief   切割字符串
 * @param   ch：源字符串
 *          pos：切割的起始位置
 *          length：切割长度
 * @return  切割后的字符串
 */
static char* sub_string(char* ch, int pos, int length)
{  
    /* 定义字符指针 指向传递进来的ch地址 */
    char* pch = ch;
    /* 通过calloc来分配一个length长度的字符数组，返回的是字符指针 */
    char* sub_ch = (char*)calloc(sizeof(char), length + 1);
    int i;

    pch = pch + pos;

    /* 是pch指针指向pos位置 */
    for (i = 0; i < length; i++) {
        sub_ch[i] = *(pch++);
    }
    /* 加上字符串结束符 */
    sub_ch[length] = '\0';

    return sub_ch;
}

/**
 * @brief   判断字符串是否为纯数字。
 * @param   str：指向字符串的指针（字符串一般是通过get_str_input();得到的输入）。
 * @return  符合返回——1 不符合返回——0 错误返回错误信息
 */
static int is_num(char *str)
{
    uint8_t count;

    if (!str) {
        return PARAM_FALSE;
    }

    for (count = 0; count < INPUT_LENGTH; count++) {

        if (str[count] == '\0')
            break;
        if (str[count] < '0' || str[count] > '9')
            return FALSE;
    }
    return TRUE;
}

/**
 * @brief   判断字符串是否为大写字母和下划线。
 * @param   str：指向字符串的指针（字符串一般是通过get_str_input();得到的输入）。
 * @return  符合返回——1 不符合返回——0 错误返回错误信息
 */
static int is_uppercase_letter(char *str)
{
    uint8_t count;
    uint8_t ret;

    if (!str) {
        return PARAM_FALSE;
    }

    for (count = 0; count < INPUT_LENGTH; count++) {
        if (str[count] == '\0')
            break;
        if ((str[count] >= 'A' && str[count] <= 'Z') || str[count] == '_')
            ret = TRUE;
        else
            return FALSE;
    }

    return ret;
}